home *** CD-ROM | disk | FTP | other *** search
- /*
-
- Name:
- VIRTCH.C
-
- Description:
- All-c sample mixing routines, using a 32 bits mixing buffer
-
- Portability:
- All systems - all compilers
-
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <malloc.h>
- #include "mikmod.h"
-
- #define FRACBITS 11
- #define FRACMASK ((1L<<FRACBITS)-1)
-
- #define TICKLSIZE 3600
- #define TICKWSIZE (TICKLSIZE*2)
- #define TICKBSIZE (TICKWSIZE*2)
- static SLONG VC_TICKBUF[TICKLSIZE];
-
- #ifndef min
- #define min(a,b) (((a)<(b)) ? (a) : (b))
- #endif
-
- typedef struct{
- UBYTE kick; /* =1 -> sample has to be restarted */
- UBYTE active; /* =1 -> sample is playing */
- UWORD flags; /* 16/8 bits looping/one-shot */
- SWORD handle; /* identifies the sample */
- ULONG start; /* start index */
- ULONG size; /* samplesize */
- ULONG reppos; /* loop start */
- ULONG repend; /* loop end */
- ULONG frq; /* current frequency */
- UBYTE vol; /* current volume */
- UBYTE pan; /* current panning position */
- SLONG current; /* current index in the sample */
- SLONG increment; /* fixed-point increment value */
- #ifdef __WATCOMC__
- UWORD lvolsel; /* left volume table selector */
- UWORD rvolsel; /* right volume table selector */
- #else
- SLONG lvolmul; /* left volume multiply */
- SLONG rvolmul; /* right volume multiply */
- #endif
- } VINFO;
-
-
- static VINFO vinf[32];
- static VINFO *vnf;
-
- static UWORD samplesthatfit;
- static SLONG idxsize,idxlpos,idxlend,maxvol;
-
- static long per256;
- static int ampshift;
-
-
- #ifdef __WATCOMC__
-
- static SLONG voltab[65][256];
- static UWORD volsel[65];
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- UWORD lvolsel,rvolsel;
-
- void AsmStereoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,ULONG todo);
- #pragma aux AsmStereoNormal \
- parm [esi] [edi] [ebx] [ecx] [edx] \
- modify [eax];
-
-
- void AsmMonoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,ULONG todo);
- #pragma aux AsmMonoNormal \
- parm [esi] [edi] [ebx] [ecx] [edx] \
- modify [eax];
-
- #ifdef __cplusplus
- }
- #endif
-
- void freedescriptor(unsigned short selector);
- #pragma aux freedescriptor = \
- "mov ax,0001h" \
- "int 31h" \
- parm [bx] \
- modify [ax];
-
- unsigned short getalias(void);
- #pragma aux getalias = \
- "mov ax,cs " \
- "mov bx,ax " \
- "mov ax,000ah" \
- "int 31h " \
- "jnc isok " \
- "xor ax,ax " \
- "isok: " \
- modify [bx] \
- value [ax];
-
- void setbase(unsigned short selector,unsigned long offset);
- #pragma aux setbase = \
- "mov ax,0007h" \
- "mov ecx,edx" \
- "ror ecx,16" \
- "int 31h" \
- parm [bx] [edx] \
- modify [ax ecx] ;
-
- void VC_Sample32To16Copy(SLONG *srce,SWORD *dest,ULONG count,UBYTE shift);
- #pragma aux VC_Sample32To16Copy = \
- "again:" \
- "mov eax,[esi]" \
- "sar eax,cl" \
- "cmp eax,32767" \
- "jg toobig" \
- "cmp eax,-32768" \
- "jl toosmall" \
- "write:" \
- "mov [edi],ax" \
- "add esi,4" \
- "add edi,2" \
- "dec edx" \
- "jnz again" \
- "jmp ready" \
- "toobig:" \
- "mov eax,32767" \
- "jmp write" \
- "toosmall:" \
- "mov eax,-32768" \
- "jmp write" \
- "ready:" \
- parm [esi] [edi] [edx] [cl] \
- modify [eax] ;
-
-
- void VC_Sample32To8Copy(SLONG *srce,SBYTE *dest,ULONG count,UBYTE shift);
- #pragma aux VC_Sample32To8Copy = \
- "again:" \
- "mov eax,[esi]" \
- "sar eax,cl" \
- "cmp eax,127" \
- "jg toobig" \
- "cmp eax,-128" \
- "jl toosmall" \
- "write:" \
- "add al,080h" \
- "mov [edi],al" \
- "add esi,4" \
- "inc edi" \
- "dec edx" \
- "jnz again" \
- "jmp ready" \
- "toobig:" \
- "mov eax,127" \
- "jmp write" \
- "toosmall:" \
- "mov eax,-128" \
- "jmp write" \
- "ready:" \
- parm [esi] [edi] [edx] [cl] \
- modify [eax] ;
-
-
- #else
-
-
- static SLONG lvolmul,rvolmul;
-
-
- static void VC_Sample32To8Copy(SLONG *srce,SBYTE *dest,ULONG count,UBYTE shift)
- {
- SLONG c;
- int shift=(24-ampshift);
-
- while(count--){
- c=*srce >> shift;
- if(c>127) c=127;
- else if(c<-128) c=-128;
- *dest++=c+128;
- srce++;
- }
- }
-
-
- static void VC_Sample32To16Copy(SLONG *srce,SWORD *dest,ULONG count,UBYTE shift)
- {
- SLONG c;
- int shift=(16-ampshift);
-
- while(count--){
- c=*srce >> shift;
- if(c>32767) c=32767;
- else if(c<-32768) c=-32768;
- *dest++=c;
- srce++;
- }
- }
-
- #endif
-
-
- static SLONG fraction2long(ULONG dividend,UWORD divisor)
- /*
- Converts the fraction 'dividend/divisor' into a fixed point longword.
- */
- {
- ULONG whole,part;
-
- whole=dividend/divisor;
- part=((dividend%divisor)<<FRACBITS)/divisor;
-
- return((whole<<FRACBITS)|part);
- }
-
-
- static UWORD samples2bytes(UWORD samples)
- {
- if(md_mode & DMODE_16BITS) samples<<=1;
- if(md_mode & DMODE_STEREO) samples<<=1;
- return samples;
- }
-
-
- static UWORD bytes2samples(UWORD bytes)
- {
- if(md_mode & DMODE_16BITS) bytes>>=1;
- if(md_mode & DMODE_STEREO) bytes>>=1;
- return bytes;
- }
-
-
- /**************************************************
- ***************************************************
- ***************************************************
- **************************************************/
-
-
- static SBYTE *Samples[MAXSAMPLEHANDLES];
-
-
- BOOL LargeRead(SBYTE *buffer,ULONG size)
- {
- int t;
- ULONG todo;
-
- while(size){
- /* how many bytes to load (in chunks of 8000) ? */
-
- todo=(size>8000)?8000:size;
-
- /* read data */
-
- SL_Load(buffer,todo);
- /* and update pointers.. */
-
- size-=todo;
- buffer+=todo;
- }
- return 1;
- }
-
-
-
- SWORD VC_SampleLoad(FILE *fp,ULONG length,ULONG reppos,ULONG repend,UWORD flags)
- {
- int handle;
- ULONG t;
-
- SL_Init(fp,flags,(flags|SF_SIGNED)&~SF_16BITS);
-
- /* Find empty slot to put sample address in */
-
- for(handle=0;handle<MAXSAMPLEHANDLES;handle++){
- if(Samples[handle]==NULL) break;
- }
-
- if(handle==MAXSAMPLEHANDLES){
- myerr=ERROR_OUT_OF_HANDLES;
- return -1;
- }
-
- if((Samples[handle]=(SBYTE *)malloc(length+16))==NULL){
- myerr=ERROR_SAMPLE_TOO_BIG;
- return -1;
- }
-
- /* read sample into buffer. */
- LargeRead(Samples[handle],length);
-
- /* Unclick samples: */
-
- if(flags & SF_LOOP){
- if(flags & SF_BIDI)
- for(t=0;t<16;t++) Samples[handle][repend+t]=Samples[handle][(repend-t)-1];
- else
- for(t=0;t<16;t++) Samples[handle][repend+t]=Samples[handle][t+reppos];
- }
- else{
- for(t=0;t<16;t++) Samples[handle][t+length]=0;
- }
-
- return handle;
- }
-
-
-
- void VC_SampleUnload(SWORD handle)
- {
- void *sampleadr=Samples[handle];
-
- free(sampleadr);
- Samples[handle]=NULL;
- }
-
-
- /**************************************************
- ***************************************************
- ***************************************************
- **************************************************/
-
-
- #ifndef __WATCOMC__
-
-
- static void (*SampleMix)(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo);
-
-
- static void MixStereoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo)
- {
- SBYTE sample;
-
- while(todo>0){
- sample=srce[index>>FRACBITS];
- *(dest++)+=lvolmul*sample;
- *(dest++)+=rvolmul*sample;
- index+=increment;
- todo--;
- }
- }
-
-
- static void MixMonoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo)
- {
- SBYTE sample;
-
- while(todo>0){
- sample=srce[index>>FRACBITS];
- *(dest++)+=lvolmul*sample;
- index+=increment;
- todo--;
- }
- }
-
-
- static void MixStereoInterp(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo)
- {
- SWORD sample,a,b;
-
- while(todo>0){
- a=srce[index>>FRACBITS];
- b=srce[1+(index>>FRACBITS)];
- sample=a+(((long)(b-a)*(index&FRACMASK))>>FRACBITS);
-
- *(dest++)+=lvolmul*sample;
- *(dest++)+=rvolmul*sample;
- index+=increment;
- todo--;
- }
- }
-
-
- static void MixMonoInterp(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo)
- {
- SWORD sample,a,b;
-
- while(todo>0){
- a=srce[index>>FRACBITS];
- b=srce[1+(index>>FRACBITS)];
- sample=a+(((long)(b-a)*(index&FRACMASK))>>FRACBITS);
-
- *(dest++)+=lvolmul*sample;
-
- index+=increment;
- todo--;
- }
- }
-
- #endif
-
-
- static UWORD NewPredict(SLONG index,SLONG end,SLONG increment,UWORD todo)
- /*
- This functions returns the number of resamplings we can do so that:
-
- - it never accesses indexes bigger than index 'end'
- - it doesn't do more than 'todo' resamplings
- */
- {
- SLONG di;
-
- di=(end-index)/increment;
- index+=(di*increment);
-
- if(increment<0){
- while(index>=end){
- index+=increment;
- di++;
- }
- }
- else{
- while(index<=end){
- index+=increment;
- di++;
- }
- }
- return ((di<todo) ? di : todo);
- }
-
-
- static void VC_AddChannel(SLONG *ptr,UWORD todo)
- /*
- Mixes 'todo' stereo or mono samples of the current channel to the tickbuffer.
- */
- {
- SLONG end;
- UWORD done,needs;
- SBYTE *s;
-
- while(todo>0){
-
- /* update the 'current' index so the sample loops, or
- stops playing if it reached the end of the sample */
-
- if(vnf->flags&SF_REVERSE){
-
- /* The sample is playing in reverse */
-
- if(vnf->flags&SF_LOOP){
-
- /* the sample is looping, so check if
- it reached the loopstart index */
-
- if(vnf->current<idxlpos){
- if(vnf->flags&SF_BIDI){
-
- /* sample is doing bidirectional loops, so 'bounce'
- the current index against the idxlpos */
-
- vnf->current=idxlpos+(idxlpos-vnf->current);
- vnf->flags&=~SF_REVERSE;
- vnf->increment=-vnf->increment;
- }
- else
- /* normal backwards looping, so set the
- current position to loopend index */
-
- vnf->current=idxlend-(idxlpos-vnf->current);
- }
- }
- else{
-
- /* the sample is not looping, so check
- if it reached index 0 */
-
- if(vnf->current<0){
-
- /* playing index reached 0, so stop
- playing this sample */
-
- vnf->current=0;
- vnf->active=0;
- break;
- }
- }
- }
- else{
-
- /* The sample is playing forward */
-
- if(vnf->flags&SF_LOOP){
-
- /* the sample is looping, so check if
- it reached the loopend index */
-
- if(vnf->current>idxlend){
- if(vnf->flags&SF_BIDI){
-
- /* sample is doing bidirectional loops, so 'bounce'
- the current index against the idxlend */
-
- vnf->flags|=SF_REVERSE;
- vnf->increment=-vnf->increment;
- vnf->current=idxlend-(vnf->current-idxlend); /* ?? */
- }
- else
- /* normal backwards looping, so set the
- current position to loopend index */
-
- vnf->current=idxlpos+(vnf->current-idxlend);
- }
- }
- else{
-
- /* sample is not looping, so check
- if it reached the last position */
-
- if(vnf->current>idxsize){
-
- /* yes, so stop playing this sample */
-
- vnf->current=0;
- vnf->active=0;
- break;
- }
- }
- }
-
- /* Vraag een far ptr op van het sampleadres
- op byte offset vnf->current, en hoeveel samples
- daarvan geldig zijn (VOORDAT segment overschrijding optreed) */
-
- if(!(s=Samples[vnf->handle])){
- vnf->current=0;
- vnf->active=0;
- break;
- }
-
- if(vnf->flags & SF_REVERSE)
- end = (vnf->flags & SF_LOOP) ? idxlpos : 0;
- else
- end = (vnf->flags & SF_LOOP) ? idxlend : idxsize;
-
- /* Als de sample simpelweg niet beschikbaar is, of als
- sample gestopt moet worden sample stilleggen en stoppen */
- /* mix 'em: */
-
- done=NewPredict(vnf->current,end,vnf->increment,todo);
-
- if(!done){
- /* printf("predict stopped it. current %ld, end %ld\n",vnf->current,end);
- */ vnf->active=0;
- break;
- }
-
- /* optimisation: don't mix anything if volume is zero */
-
- if(vnf->vol){
- #ifdef __WATCOMC__
- if(md_mode & DMODE_STEREO)
- AsmStereoNormal(s,ptr,vnf->current,vnf->increment,done);
- else
- AsmMonoNormal(s,ptr,vnf->current,vnf->increment,done);
- #else
- SampleMix(s,ptr,vnf->current,vnf->increment,done);
- #endif
- }
- vnf->current+=(vnf->increment*done);
-
- todo-=done;
- ptr+=(md_mode & DMODE_STEREO) ? (done<<1) : done;
- }
- }
-
-
-
-
- static void VC_FillTick(SBYTE *buf,UWORD todo)
- /*
- Mixes 'todo' samples to 'buf'.. The number of samples has
- to fit into the tickbuffer.
- */
- {
- int t;
-
- /* clear the mixing buffer: */
-
- memset(VC_TICKBUF,0,(md_mode & DMODE_STEREO) ? todo<<3 : todo<<2);
-
- for(t=0;t<md_numchn;t++){
- vnf=&vinf[t];
-
- if(vnf->active){
- idxsize=(vnf->size<<FRACBITS)-1;
- idxlpos=vnf->reppos<<FRACBITS;
- idxlend=(vnf->repend<<FRACBITS)-1;
- #ifdef __WATCOMC__
- lvolsel=vnf->lvolsel;
- rvolsel=vnf->rvolsel;
- #else
- lvolmul=vnf->lvolmul;
- rvolmul=vnf->rvolmul;
- #endif
- VC_AddChannel(VC_TICKBUF,todo);
- }
- }
-
- if(md_mode & DMODE_16BITS)
- VC_Sample32To16Copy(VC_TICKBUF,(SWORD *)buf,(md_mode & DMODE_STEREO) ? todo<<1 : todo,16-ampshift);
- else
- VC_Sample32To8Copy(VC_TICKBUF,buf,(md_mode & DMODE_STEREO) ? todo<<1 : todo,24-ampshift);
- }
-
-
-
- static void VC_WritePortion(SBYTE *buf,UWORD todo)
- /*
- Writes 'todo' mixed SAMPLES (!!) to 'buf'. When todo is bigger than the
- number of samples that fit into VC_TICKBUF, the mixing operation is split
- up into a number of smaller chunks.
- */
- {
- UWORD part;
-
- /* write 'part' samples to the buffer */
-
- while(todo){
- part=min(todo,samplesthatfit);
- VC_FillTick(buf,part);
- buf+=samples2bytes(part);
- todo-=part;
- }
- }
-
-
- static UWORD TICKLEFT;
-
-
- void VC_WriteSamples(SBYTE *buf,UWORD todo)
- {
- int t;
- UWORD part;
-
- while(todo>0){
-
- if(TICKLEFT==0){
- md_player();
-
- TICKLEFT=(125L*md_mixfreq)/(50L*md_bpm);
-
- /* compute volume, frequency counter & panning parameters for each channel. */
-
- for(t=0;t<md_numchn;t++){
- int pan,vol,lvol,rvol;
-
- if(vinf[t].kick){
- vinf[t].current=(vinf[t].start << FRACBITS);
- vinf[t].active=1;
- vinf[t].kick=0;
- }
-
- if(vinf[t].frq==0) vinf[t].active=0;
-
- if(vinf[t].active){
- vinf[t].increment=fraction2long(vinf[t].frq,md_mixfreq);
-
- if(vinf[t].flags & SF_REVERSE) vinf[t].increment=-vinf[t].increment;
-
- vol=vinf[t].vol;
- pan=vinf[t].pan;
-
- #ifdef __WATCOMC__
- if(md_mode & DMODE_STEREO){
- lvol= ( vol * (255-pan) ) / 255;
- rvol= ( vol * pan ) / 255;
- vinf[t].lvolsel=volsel[lvol];
- vinf[t].rvolsel=volsel[rvol];
- }
- else{
- vinf[t].lvolsel=volsel[vol];
- }
- #else
- if(md_mode & DMODE_STEREO){
- lvol= ( vol * (255-pan) ) / 255;
- rvol= ( vol * pan ) / 255;
- vinf[t].lvolmul=(maxvol*lvol)/64;
- vinf[t].rvolmul=(maxvol*rvol)/64;
- }
- else{
- vinf[t].lvolmul=(maxvol*vol)/64;
- }
- #endif
- }
- }
- }
-
- part=min(TICKLEFT,todo);
-
- VC_WritePortion(buf,part);
-
- TICKLEFT-=part;
- todo-=part;
-
- buf+=samples2bytes(part);
- }
- }
-
-
- UWORD VC_WriteBytes(SBYTE *buf,UWORD todo)
- /*
- Writes 'todo' mixed SBYTES (!!) to 'buf'. It returns the number of
- SBYTES actually written to 'buf' (which is rounded to number of samples
- that fit into 'todo' bytes).
- */
- {
- todo=bytes2samples(todo);
- VC_WriteSamples(buf,todo);
- return samples2bytes(todo);
- }
-
-
- void VC_SilenceBytes(SBYTE *buf,UWORD todo)
- /*
- Fill the buffer with 'todo' bytes of silence (it depends on the mixing
- mode how the buffer is filled)
- */
- {
- /* clear the buffer to zero (16 bits
- signed ) or 0x80 (8 bits unsigned) */
-
- if(md_mode & DMODE_16BITS)
- memset(buf,0,todo);
- else
- memset(buf,0x80,todo);
- }
-
-
- void VC_PlayStart(void)
- {
- int t;
-
- maxvol=16777216L / md_numchn;
-
- #ifdef __WATCOMC__
- for(t=0;t<65;t++){
- int c;
- SLONG volmul=(maxvol*t)/64;
- for(c=-128;c<128;c++){
- voltab[t][(UBYTE)c]=(SLONG)c*volmul;
- }
- }
- #endif
-
- /* instead of using a amplifying lookup table, I'm using a simple shift
- amplify now.. amplifying doubles with every extra 4 channels, and also
- doubles in stereo mode.. this seems to give similar volume levels
- across the channel range */
-
- ampshift=md_numchn/8;
- if(md_mode & DMODE_STEREO) ampshift++;
-
- #ifndef __WATCOMC__
- if(md_mode & DMODE_INTERP)
- SampleMix=(md_mode & DMODE_STEREO) ? MixStereoInterp : MixMonoInterp;
- else
- SampleMix=(md_mode & DMODE_STEREO) ? MixStereoNormal : MixMonoNormal;
- #endif
-
- samplesthatfit=TICKLSIZE;
- if(md_mode & DMODE_STEREO) samplesthatfit>>=1;
- TICKLEFT=0;
- }
-
-
- void VC_PlayStop(void)
- {
- }
-
-
- BOOL VC_Init(void)
- {
- int t;
- for(t=0;t<32;t++){
- vinf[t].current=0;
- vinf[t].flags=0;
- vinf[t].handle=0;
- vinf[t].kick=0;
- vinf[t].active=0;
- vinf[t].frq=10000;
- vinf[t].vol=0;
- vinf[t].pan=(t&1)?0:255;
- }
-
- #ifdef __WATCOMC__
- if(md_mode & DMODE_INTERP) md_mode&=~DMODE_INTERP;
-
- for(t=0;t<65;t++) volsel[t]=0;
-
- for(t=0;t<65;t++){
- if(!(volsel[t]=getalias())) return 0;
- setbase(volsel[t],(ULONG)voltab[t]);
- }
- #endif
-
- return 1;
- }
-
-
- void VC_Exit(void)
- {
- #ifdef __WATCOMC__
- int t;
- for(t=0;t<65;t++){
- if(volsel[t]) freedescriptor(volsel[t]);
- }
- #endif
- }
-
-
- void VC_VoiceSetVolume(UBYTE voice,UBYTE vol)
- {
- vinf[voice].vol=vol;
- }
-
-
- void VC_VoiceSetFrequency(UBYTE voice,ULONG frq)
- {
- vinf[voice].frq=frq;
- }
-
-
- void VC_VoiceSetPanning(UBYTE voice,UBYTE pan)
- {
- vinf[voice].pan=pan;
- }
-
-
- void VC_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags)
- {
- if(start>=size) return;
-
- if(flags&SF_LOOP){
- if(repend>size) repend=size; /* repend can't be bigger than size */
- }
-
- vinf[voice].flags=flags;
- vinf[voice].handle=handle;
- vinf[voice].start=start;
- vinf[voice].size=size;
- vinf[voice].reppos=reppos;
- vinf[voice].repend=repend;
- vinf[voice].kick=1;
- }
-